#include "cmesh.h"

/**
 * CMesh - constructor
 */
CMesh::CMesh() {
  verts = NULL;
  faces = NULL;
  coords = NULL;
  indices = NULL;
  normals = NULL;
  
  numOfVerts = 0;
  numOfCoords = 0;
  numOfFaces = 0;
}
//---------------------------------------------------------------------------

/**
 * ~CMesh - destructor
 */
CMesh::~CMesh() {
  free();
}
//---------------------------------------------------------------------------

/**
 * setStrName - sets name of mesh
 * @param strName
 */
void CMesh::setStrName(string strName) {
  this->strName = strName;
}
//---------------------------------------------------------------------------

/**
 * setMaterialId - sets id of mesh material
 * @param materialId
 */
void CMesh::setMaterialId(int materialId) {
  this->materialId = materialId;
}
//---------------------------------------------------------------------------

/**
 * getStrName - returns name of mesh
 * @return mesh name
 */
string CMesh::getStrName() const {
  return strName;
}
//---------------------------------------------------------------------------

/**
 * getMaterialId - returns id of material
 * @returnmaterial id
 */
int CMesh::getMaterialId() const {
  return materialId;
}
//---------------------------------------------------------------------------

/**
 * getNumOfVerts - returns num of mesh vertices
 * @return num of mesh vertices
 */
int CMesh::getNumOfVerts() const {
  return numOfVerts;
}
//---------------------------------------------------------------------------

/**
 * getNumOfCoords - returns num of mesh texture coords
 * @return num of texture coords
 */
int CMesh::getNumOfCoords() const {
  return numOfCoords;
}
//---------------------------------------------------------------------------

/**
 * getNumOfPolygons - returns num of mesh faces
 * @return num of mesh faces
 */
int CMesh::getNumOfFaces() const {
  return numOfFaces;
}
//---------------------------------------------------------------------------

/**
 * getVertsPtr - returns pointer on vertices of mesh
 * @return pointer on mesh vertices
 */
CVector3D<float>* CMesh::getVertsPtr() const {
  return verts;
}
//---------------------------------------------------------------------------

/**
 * getCoordsPtr - returns pointer on texture coords of mesh
 * @return pointer on mesh texture coords
 */
CVector2D<float>* CMesh::getCoordsPtr() const {
  return coords;
}
//---------------------------------------------------------------------------

/**
 * getNormalsPtr - returns pointer on surface normals of mesh
 * @return pointer on mesh surface normals
 */
CVector3D<float>* CMesh::getNormalsPtr() const {
  return normals;
}
//---------------------------------------------------------------------------

/**
 * getFacesPtr - returns pointer on faces of mesh
 * @return pointer on mesh faces
 */
TTriangle* CMesh::getFacesPtr() const {
  return faces;
}
//---------------------------------------------------------------------------

/**
 * getIndicesPtr - returns pointer on indices of mesh vertices
 * @return pointer on indices of mesh vertices
 */
int* CMesh::getIndicesPtr() const {
  return indices;
}
//---------------------------------------------------------------------------

/**
 * createVerts - creates given number of mesh vertices
 * @param numOfVerts
 * @return pointer on created vertices
 */
CVector3D<float>* CMesh::createVerts(int numOfVerts) {  
  
  // Delete primal vertices
  deleteVerts();
  
  // Create vertices, their indices and surface normals
  this->numOfVerts = numOfVerts;
  verts = new CVector3D<float>[numOfVerts];
  normals = new CVector3D<float>[numOfVerts];
  indices = new int[numOfVerts];
  return verts;
}
//---------------------------------------------------------------------------

/**
 * createCoords - creates given number of mesh texture coords
 * @param numOfCoords
 * @return pointer on created coords
 */
CVector2D<float>* CMesh::createCoords(int numOfCoords) {
  
  // Delete primal texture coords
  deleteCoords();
  
  // Create texture coords
  this->numOfCoords = numOfCoords;
  coords = new CVector2D<float>[numOfCoords];
  return coords;
}
//---------------------------------------------------------------------------

/**
 * createFaces - creates given number of mesh faces
 * @param numOfFaces
 * @return pointer on created faces
 */
TTriangle* CMesh::createFaces(int numOfFaces) {
  
  // Delete primal faces
  deleteFaces();
  
  // Create faces
  this->numOfFaces = numOfFaces;
  faces = new TTriangle[numOfFaces];
  return faces;
}
//---------------------------------------------------------------------------

/**
 * computeIndices - computes indices of mesh vertex
 */
void CMesh::computeIndices() {

  // Assign indices to vertex
  for (int i = 0; i < numOfFaces * 3; i += 3) {
    indices[i] = faces[i/3].vertIds[0];
    indices[i+1] = faces[i/3].vertIds[1];
    indices[i+2] = faces[i/3].vertIds[2];
  }
}
//---------------------------------------------------------------------------

/**
 * computeNormals - computes surface normals of mesh faces
 */
void CMesh::computeNormals() {

  // Temporary array of surface normals
  CVector3D<float>* tmpNormals = new CVector3D<float>[numOfFaces];

  // Compute surface normal for each face of mesh
  for (int i = 0; i < numOfFaces; i++) {

    // Save vertices of currently selected face
    CVector3D<float> faceVerts[3];
    faceVerts[0] = verts[faces[i].vertIds[0]];
    faceVerts[1] = verts[faces[i].vertIds[1]];
    faceVerts[2] = verts[faces[i].vertIds[2]];

    // Pick two vectors in surface of selected face
    CVector3D<float> v1(faceVerts[0] - faceVerts[2]);
    CVector3D<float> v2(faceVerts[2] - faceVerts[1]);

    // Create normalized surface normal
    tmpNormals[i] = CVector3D<float>::cross(v1, v2);
  }

  // Create group of surface normals without repeating concurrent vectors
  for (int i = 0; i < numOfVerts; i++) {
    
    // Temporary variables
    int sharedCount(0);
    CVector3D<float> sum;
    sum.clearToZero();
    
    // Compute how many faces share currently selected vector
    for (int j = 0; j < numOfFaces; j++)  {
      if (faces[j].vertIds[0] == i || faces[j].vertIds[1] == i ||
          faces[j].vertIds[2] == i) {
        sum += tmpNormals[j];
        sharedCount++;
      }
    }

    // Compute surface normal of polygon facing vertex, as opposite value of
    // arithmetic mean of surface normals of polygon, which possesses the
    // vertex
    normals[i] = sum / float(-sharedCount);
    normals[i].normalize();
  }
  
  delete [] tmpNormals;
}
//---------------------------------------------------------------------------

/**
 * deleteVerts - deletes list of vertices and data related to them
 */
void CMesh::deleteVerts() {

  // Free vertices
  if (verts != NULL) {
    delete [] verts;
    verts = NULL;
  }
  
  // Free indices of vertices
  if (indices != NULL) {
    delete [] indices;
    indices = NULL;
  }
  
  // Free surface normals
  if (normals != NULL) {
    delete [] normals;
    normals = NULL;
  }
  
  this->numOfVerts = 0;
}
//---------------------------------------------------------------------------

/**
 * deleteCoords - deletes list of texture coords
 */
void CMesh::deleteCoords() {
    
  // Free texture coords
  if (coords != NULL) {
    delete [] coords;
    coords = NULL;
  }
  
  this->numOfCoords = 0;
}
//---------------------------------------------------------------------------

/**
 * deleteFaces - deletes list of faces
 */
void CMesh::deleteFaces() {

  // Free faces
  if (faces != NULL) {
    delete [] faces;
    faces = NULL;
  }
  
  this->numOfFaces = 0;
}
//---------------------------------------------------------------------------

/**
 * free - deletes mesh
 */
void CMesh::free() {  
  deleteVerts();
  deleteCoords();
  deleteFaces();
}
//---------------------------------------------------------------------------
